﻿#include "CQtLog.h"

#include <QCoreApplication>
#include <QTime>
#include <QDebug>
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/fmt/bundled/color.h"

#define LOG_FILENAME_PREFIX     "log/log"
#define LOG_FILENAME            LOG_FILENAME_PREFIX"_yyyy-MM-dd-HHmmss"

static std::shared_ptr<spdlog::details::thread_pool> tp;
std::shared_ptr<spdlog::logger> CQtLog::logger_;
static void outputMessage(QtMsgType level, const QMessageLogContext & context, const QString & msg)
{
    const QString & message = qFormatLogMessage(level, context, msg);
    const auto & logMsg = message.toStdString();
    // const auto & logMsg = qPrintable(message);
    switch (level)
    {
        case QtDebugMsg: QtLogger->debug(logMsg); break;
        case QtInfoMsg: QtLogger->info(logMsg); break;
        case QtWarningMsg: QtLogger->warn(logMsg); break;
        case QtCriticalMsg: QtLogger->error(logMsg); break;
        case QtFatalMsg: QtLogger->critical(logMsg); break;
        default: QtLogger->debug(logMsg); break;
    }
}

static void closeLogFile()
{
    if (nullptr != QtLogger)
    {
        QtLogger->info("flush log {}", QtLogger->name());
        QtLogger->flush();
    }
    spdlog::drop_all();
}

#define DEBUG_COLOR
#define INFO_COLOR
#define WARN_COLOR      "\033[1;40;33m"
#define ERROR_COLOR     "\033[1;40;31m"
#define CLOSE_COLOR     "\033[0m"

#define DEBUG_LEVEL     "D"
#define INFO_LEVEL      "I"
#define ERROR_LEVEL     "E"

void CQtLog::init(bool print, bool save, bool saveDaily, bool is_async)
{
    if (!print && !save) { qWarning() << "Error!! print and save are both false"; return; }
    if (nullptr != logger_) { qWarning() << "Error!! already initialized"; return; }

    qSetMessagePattern("%{if-warning}" WARN_COLOR "%{endif}"
                       "%{if-critical}" ERROR_COLOR "%{endif}"
                       "%{if-fatal}" ERROR_COLOR "%{endif}"
                       // "[%{if-debug}" DEBUG_LEVEL "%{endif}"
                       // "%{if-info}" INFO_LEVEL "%{endif}"
                       // "%{if-warning}" ERROR_LEVEL "%{endif}"
                       // "%{if-critical}C%{endif}"
                       // "%{if-fatal}F%{endif}"
                       // " %{time yyyy-MM-dd hh:mm:ss.zzz}] "
                       "%{message}"
                       " [%{function}() %{file}:%{line}]"
                       CLOSE_COLOR);
    //注册MessageHandler
    qInstallMessageHandler(outputMessage);
    spdlog::set_level(spdlog::level::debug); // Set global log level to debug

    // const spdlog::sinks_init_list sink_list = {console_sink, file_sink};
    std::vector<spdlog::sink_ptr> sinks;
    if (print)
    {
        auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
        sinks.push_back(console_sink); // 注册控制台接收器为默认的接收器
        // console_sink->set_color(spdlog::level::warn, fmt::terminal_color::red);
        // console_sink->set_color(spdlog::level::warn, spdlog::sinks::stdout_color_sink_mt::red);
        console_sink->set_color(spdlog::level::warn, "\033[31m");
    }

    if (save)
    {
        if (saveDaily)
        {
            auto file_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("log/log.txt", 0, 0, true);
            sinks.push_back(file_sink);
        }
        else
        {
            const auto time = QDateTime::currentDateTime().toString(LOG_FILENAME);
            const auto fileName = QString("%1.%2.txt").arg(time).arg(QCoreApplication::applicationPid()).toStdString();
            auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(fileName);
            sinks.push_back(file_sink);
        }
    }

    if (is_async)
    {
        tp = std::make_shared<spdlog::details::thread_pool>(spdlog::details::default_async_q_size, 1U);
        logger_ = std::make_shared<spdlog::async_logger>("async multi_sink", sinks.begin(), sinks.end(), tp /*, spdlog::async_overflow_policy::discard_new*/);
    }
    else { logger_ = std::make_shared<spdlog::logger>("multi_sink", sinks.begin(), sinks.end()); }

    logger_->set_level(spdlog::level::debug);
    // logger_->set_pattern("[%L %Y-%m-%d %H:%M:%S.%f %^%t%$] %v");
    logger_->set_pattern("[%L %H:%M:%S.%f %^%t%$] %v");
    spdlog::set_default_logger(logger_);
}

void CQtLog::close()
{
    closeLogFile();
    qInstallMessageHandler(0);
}

static spdlog::level::level_enum QtMsgType2Level(QtMsgType level)
{
    switch (level)
    {
    case QtDebugMsg: return spdlog::level::trace;
    case QtInfoMsg: return spdlog::level::info;
    case QtWarningMsg: return spdlog::level::warn;
    case QtCriticalMsg: return spdlog::level::err;
    case QtFatalMsg: return spdlog::level::critical;
    default: return spdlog::level::trace;
    }
}


void CQtLog::setLevel(QtMsgType level) { spdlog::set_level(QtMsgType2Level(level)); }

void CQtLog::setIsInstallMsgHandler(bool isInstallMsgHandler)
{
    qInstallMessageHandler(isInstallMsgHandler ? outputMessage : nullptr);
}
